#include "ImageLoader.hpp"
#include "General Utils.hpp"

// Static declarations
vector<ImageLoader::Image>					ImageLoader::mImages;
map<string, int>							ImageLoader::mImageNameToID;
vector<string>								ImageLoader::mUsedPaths;
map<string, vector<ImageLoader::SubBase> >	ImageLoader::mAnimationBases;
map<int	, ImageLoader::MinimapData>			ImageLoader::mMinimapData;


ImageLoader* ImageLoader::Instance()
{
	static ImageLoader instance;
	return &instance;
}

// Add a minimap key
void ImageLoader::AddMinimapKey(string imageName, float r, float g, float b)
{
	MinimapData newData;
	newData.imageID = mImageNameToID[imageName];

	// correct numbers to be in OGL format
	if(r > 1.0f)
		r /= 255.0f;

	if(g > 1.0f)
		g /= 255.0f;

	if(b > 1.0f)
		b /= 255.0f;

	newData.r = r;
	newData.g = g;
	newData.b = b;

	mMinimapData.insert(std::pair<int, MinimapData>(newData.imageID, newData));
}

// Animation
void ImageLoader::AnimCreate(string baseName)
{
	vector<SubBase> allSubBases;
	vector<int> frameIDs;

	SubBase base;
	base.FrameIDs = frameIDs;
	base.TotalFrames = 0;
	base.UpdateTime = 0;

	base.AnimationType = "walk";
	allSubBases.push_back(base);

	mAnimationBases.insert(std::pair<string, vector<SubBase>>(baseName, allSubBases));

}

// POTENTIAL ERROR WITH -1
void ImageLoader::AnimAdd(string baseName, string subBaseName, string imageName)
{
	for(unsigned int i = 0; i < mAnimationBases[baseName].size(); ++i) // Iterates through all of the SubBases
		if(mAnimationBases[baseName][i].AnimationType == subBaseName) // If this is the correct SubBase
		{
			mAnimationBases[baseName][i].FrameIDs.push_back(mImageNameToID[imageName]);
			mAnimationBases[baseName][i].TotalFrames++;
			return;
		}
		
		assert("Invalid SubBase in ImageLoader::AnimAdd" && false);
}
vector<ImageLoader::SubBase> ImageLoader::GetAnimation(string baseName)
{
	return mAnimationBases[baseName];
}

ImageLoader::SubBase ImageLoader::GetSubBase(string baseName, string subBaseName)
{
	for(unsigned int i = 0; i < mAnimationBases[baseName].size(); ++i) // Iterates through all of the SubBases
		if(mAnimationBases[baseName][i].AnimationType == subBaseName) // If this is the correct SubBase
		{
			return mAnimationBases[baseName][i];
		}
}

void ImageLoader::AnimChange(string baseName, string subBaseName, string member, int newData)
{
	for(unsigned int i = 0; i < mAnimationBases[baseName].size(); ++i) // Iterates through all of the SubBases
		if(mAnimationBases[baseName][i].AnimationType == subBaseName) // If this is the correct SubBase
		{
			if(member == "UpdateTime")
				mAnimationBases[baseName][i].UpdateTime = newData;
		}
}

void ImageLoader::DeleteAllAnimations()
{
	mAnimationBases.clear();
}

void ImageLoader::DeleteTexture(int imgID)
{
	if(mImages[imgID].texture != NULL)
	{
		glDeleteTextures(1, &mImages[imgID].texture);
		mImages[imgID].texture = NULL; // Nobody likes a dangling pointer.
	}
}

void ImageLoader::DeleteAllTextures()
{
	for (unsigned int i = 0; i < mImages.size(); i++)
	{
		DeleteTexture(i);
	}
}
/*int ImageLoader::LoadGraphic(string imageName, string filename)
{
	// Basic declarations
	Image image;
	GLenum texture_format;
	GLint nOfColors;
	image.name = imageName;



	// Big or small endian?
	SDL_PixelFormat RGBAFormat;
	RGBAFormat.palette = 0; RGBAFormat.colorkey = 0; RGBAFormat.alpha = 0;
	RGBAFormat.BitsPerPixel = 32; RGBAFormat.BytesPerPixel = 4;
	#if SDL_BYTEORDER == SDL_BIG_ENDIAN
	RGBAFormat.Rmask = 0xFF000000; RGBAFormat.Rshift = 0; RGBAFormat.Rloss = 0;
	RGBAFormat.Gmask = 0x00FF0000; RGBAFormat.Gshift = 8; RGBAFormat.Gloss = 0;
	RGBAFormat.Bmask = 0x0000FF00; RGBAFormat.Bshift = 16; RGBAFormat.Bloss = 0;
	RGBAFormat.Amask = 0x000000FF; RGBAFormat.Ashift = 24; RGBAFormat.Aloss = 0;
	#else
	RGBAFormat.Rmask = 0x000000FF; RGBAFormat.Rshift = 24; RGBAFormat.Rloss = 0;
	RGBAFormat.Gmask = 0x0000FF00; RGBAFormat.Gshift = 16; RGBAFormat.Gloss = 0;
	RGBAFormat.Bmask = 0x00FF0000; RGBAFormat.Bshift = 8; RGBAFormat.Bloss = 0;
	RGBAFormat.Amask = 0xFF000000; RGBAFormat.Ashift = 0; RGBAFormat.Aloss = 0;
	#endif
	
	// Load in the image from the filename
	SDL_Surface *orig;
	orig = IMG_Load(filename.c_str());
	if(orig == NULL)
	{
		assert("Texture not correctly loaded" && false);
	}

	SDL_PixelFormat *format = orig->format;

	SDL_Surface* paddedOrig = SDL_CreateRGBSurface(SDL_SWSURFACE, nextPof2(orig->w), nextPof2(orig->h), DataLoader->WINDOW_BPP,
		RGBAFormat.Rmask, RGBAFormat.Gmask, RGBAFormat.Bmask, RGBAFormat.Amask);



	
	SDL_Surface *surface;


	// get the number of channels in the SDL surface (temp variable)
    nOfColors = orig->format->BytesPerPixel;

	if(orig->w != nextPof2(orig->w) || orig->h != nextPof2(orig->h))
	{
		if(nOfColors != 4)
		{
			SDL_BlitSurface(orig, &orig->clip_rect, paddedOrig, NULL);
			surface = SDL_ConvertSurface(paddedOrig, &RGBAFormat, SDL_SWSURFACE);
			image.padded.h = paddedOrig->h;
			image.padded.w = paddedOrig->w;
		}
		else
		{
			surface = SDL_ConvertSurface(orig, &RGBAFormat, SDL_SWSURFACE);
			image.padded.w = surface->w;
			image.padded.h = surface->h;
		}
	}
	else 
	{
		surface = SDL_ConvertSurface(orig, &RGBAFormat, SDL_SWSURFACE);
		image.padded.h = paddedOrig->h;
		image.padded.w = paddedOrig->w;
	}


	


	// Convert the surface to a format readable by openGL
	
	
	
	
	// get the number of channels in the SDL surface
	// surface->format->BytesPerPixel
    nOfColors = surface->format->BytesPerPixel;
    if (nOfColors == 4)     // contains an alpha channel
    {
            if (surface->format->Rmask == 0x000000ff)
                    texture_format = GL_RGBA;
            else
                    texture_format = GL_BGRA;
    } else if (nOfColors == 3)     // no alpha channel
    {
            if (surface->format->Rmask == 0x000000ff)
                    texture_format = GL_RGB;
            else
                    texture_format = GL_BGR;
    } else {
            assert("Loaded image is not truecolor" && false);
    }



	// Have OpenGL generate a texture object handle for us
	glGenTextures( 1, &image.texture );
 
	// Bind the texture object
	glBindTexture( GL_TEXTURE_2D, image.texture );
 
	// Set the texture's stretching properties
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
 
	// Edit the texture object's image data using the information SDL_Surface gives us
	glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, surface->w, surface->h, 0,
                      texture_format, GL_UNSIGNED_BYTE, surface->pixels );





	// Free memory
	SDL_FreeSurface(orig);
	SDL_FreeSurface(paddedOrig);
	SDL_FreeSurface(surface);

	mImages.push_back(image);
	mImageNameToID.insert(std::pair<string, int>(imageName, (mImages.size() - 1)));
	mUsedPaths.push_back(filename);
	return (mImages.size() - 1);
}*/

#include "ExecutionLog.h"

int ImageLoader::LoadGraphic(string imageName, string filename)
{
	// Basic declarations
	Image image;
	image.name = imageName;	


	ILboolean	success;
	ILuint		ilTexID;

	// bind our texture
	ilGenImages(1, &ilTexID);
	ilBindImage(ilTexID);

	// attempt to load the image
	success = ilLoadImage(filename.c_str());

	// if we did not load the image
	if (!success)
	{
		/*ILenum err = ilGetError();

		string a = err;
		Log->AddMessage("Error " + a + " when loading an image");
		a = ilGetString(err);
		Log->AddMessage("The error string is " + a);
		a = iluErrorString(err);
		Log->AddMessage("Error string was " + a);*/

		assert("Unable to load in image" && false);
		return -1;
	}
	

	// convert to RGBA
	success = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
	
	// if that failed? go to RGB
	if (!success)
	{
	  success = ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE);
	}

	image.orig.w = ilGetInteger(IL_IMAGE_WIDTH);
	image.orig.h = ilGetInteger(IL_IMAGE_HEIGHT);

	// convert to a power of two texture
	iluImageParameter(ILU_PLACEMENT, ILU_UPPER_LEFT);
	iluEnlargeCanvas(GetNextPowerOfTwo(ilGetInteger(IL_IMAGE_WIDTH)), GetNextPowerOfTwo(ilGetInteger(IL_IMAGE_HEIGHT)), ilGetInteger(IL_IMAGE_BPP));

	if(!success)
		assert("LoadGraphic was unable to convert the image to RGB(A)" && false);

	// Have OpenGL generate a texture object handle for us
	glGenTextures( 1, &image.texture );
 
	// Bind the texture object
	glBindTexture( GL_TEXTURE_2D, image.texture );
 
	// Set the texture's stretching properties
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
 
	//ilutOglTexImage(0);
	// Edit the texture object's image data using the information DevIL gives us
	glTexImage2D(GL_TEXTURE_2D, 0, ilGetInteger(IL_IMAGE_BPP), ilGetInteger(IL_IMAGE_WIDTH),
	  ilGetInteger(IL_IMAGE_HEIGHT), 0, ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE,
	  ilGetData()); /* Texture specification */

	image.padded.w = ilGetInteger(IL_IMAGE_WIDTH);
	image.padded.h = ilGetInteger(IL_IMAGE_HEIGHT);

	ilDeleteImages(1, &ilTexID); // Because we have already copied image data into texture data we can release memory used by image.

	mImages.push_back(image);
	mImageNameToID.insert(std::pair<string, int>(imageName, (mImages.size() - 1)));
	mUsedPaths.push_back(filename);
	return (mImages.size() - 1);

}